package com.pansoft.hd;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.pansoft.hd.model.BaseHDWallet;
import com.pansoft.hd.model.BtcHdWallet;
import com.pansoft.hd.model.EthHdWallet;
import com.pansoft.hd.model.HDException;
import com.pansoft.openplanet.constant.IPAssetsConstant;

import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.LegacyAddress;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.crypto.DeterministicKey;
import org.bitcoinj.crypto.HDKeyDerivation;
import org.bitcoinj.script.Script;
import org.bitcoinj.wallet.DeterministicKeyChain;
import org.bitcoinj.wallet.DeterministicSeed;
import org.bouncycastle.util.encoders.Hex;
import org.web3j.crypto.ECKeyPair;
import org.web3j.crypto.Keys;
import org.web3j.crypto.Wallet;
import org.web3j.crypto.WalletFile;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import static com.pansoft.hd.HDWalletUtil.fromPrivateKey;
import static com.pansoft.hd.HDWalletUtil.getDeterministicSeed;
import static com.pansoft.hd.HDWalletUtil.newReceiveAddress;
import static org.bitcoinj.crypto.HDUtils.parsePath;

/**
 * HD钱包生成
 *
 * @author yqs
 * 使用了 web3j和bitcionj 库
 */
public class HDWalletGenerator {

    private static final String TAG = "HDWalletGenerator";


    /**
     * 生成HD 钱包
     *
     * @param mnemonicList      助记词
     * @param password          密码（keystore 文件的密码）
     * @param bip44Path         路径
     * @param chainEnum         链
     * @param networkParameters 网络类型
     * @param keystoreDir       keystore 文件存储目录
     * @return
     */
    public BaseHDWallet generateWallet(List<String> mnemonicList, String password,
                                       String bip44Path, IPAssetsConstant.ChainEnum chainEnum, NetworkParameters networkParameters, String keystoreDir) {
        //从助记词中获取种子
        DeterministicSeed deterministicSeed = getDeterministicSeed(mnemonicList, "");
        DeterministicKeyChain keyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build();
        BaseHDWallet hdWallet = null;
        if (chainEnum == IPAssetsConstant.ChainEnum.BTC) {
            //创建btc钱包
            hdWallet = createHdBtcWallet(deterministicSeed, bip44Path, networkParameters);
        } else {
            //创建eth 系列的钱包
            hdWallet = createETHWallet(keyChain, password, bip44Path, keystoreDir, chainEnum);
        }

        return hdWallet;
    }


    private BaseHDWallet createETHWallet(DeterministicKeyChain keyChain, String password, String bip44Path,
                                      String keystoreDir, IPAssetsConstant.ChainEnum chainEnum) {
        //"M/44H/60H/0H/0/0"
        List<ChildNumber> zeroPath = parsePath(bip44Path);
        DeterministicKey childKey0 = keyChain.getKeyByPath(zeroPath, true);
        ECKeyPair ethECKeyPair = ECKeyPair.create(childKey0.getPrivKey());
        System.out.println("钱包地址为:" + "0x" + Keys.getAddress(ethECKeyPair));
        //生成walletfile
        WalletFile walletFile = null;
        try {
            walletFile = Wallet.createLight("", ethECKeyPair);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //生成keystore文件名称
        SimpleDateFormat dateFormat = new SimpleDateFormat("'UTC--'yyyy-MM-dd'T'HH-mm-ss.SSS'--'");
        String fileName = dateFormat.format(new Date()) + walletFile.getAddress() + ".json";
        //keystore文件
        File keyStoreFile = new File(keystoreDir, fileName);
        //保存keystore 写入文件
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            objectMapper.writeValue(keyStoreFile, walletFile);
        } catch (Exception e) {
            e.printStackTrace();
            throw new HDException("保存keystore文件失败");
        }

        //钱包地址
        String ethAddress = "0x" + walletFile.getAddress();
        BaseHDWallet hdWallet = EthHdWallet.EthHDWalletBuilder.anEthHDWallet().withAddress(ethAddress)
                .withFilePath(keyStoreFile.getAbsolutePath())
                .withPrivateKey(ethECKeyPair.getPrivateKey().toString())
                //eth publickey 是 10进制的
                .withPublicKey(ethECKeyPair.getPublicKey().toString())
                .withEcKeyPair(ethECKeyPair)
                .withWalletFile(walletFile)
                .build();
        hdWallet.setChainId(chainEnum.chainId);
        hdWallet.setChainName(chainEnum.chainName);
        return hdWallet;
    }

    /**
     * 由DeterministicSeed生成HD钱包各级结构
     *
     * @param ds      seed
     * @param bipPath bip路径
     */
    public static BaseHDWallet createHdBtcWallet(DeterministicSeed ds, String bipPath, NetworkParameters networkParameters) {
        //master key
        DeterministicKey rootPrivateKey = HDKeyDerivation.createMasterPrivateKey(ds.getSeedBytes());
        System.out.println("16进制rootPrivateKey值: " + rootPrivateKey.getPrivateKeyAsHex());
        String rootKey = rootPrivateKey.serializePrivB58(networkParameters);
        System.out.println("BIP32RootKey(根密钥)值: " + rootKey);

        //deterministicKeyChain
        DeterministicKeyChain keyChain = DeterministicKeyChain.builder().seed(ds).build();
        //parent key
        DeterministicKey parent = keyChain.getKeyByPath(parsePath(bipPath), true);
        String xpub = parent.serializePubB58(networkParameters);
        String xprv = parent.serializePrivB58(networkParameters);
        System.out.println("xpub:" + xpub);
        System.out.println("xprv:" + xprv);
        //主地址key
        DeterministicKey mainAddressKey = keyChain.getKeyByPath(parsePath(bipPath + "/0/0"), true);
        //主地址
        String mainAddress;

        if (bipPath.contains("49")) {
            mainAddress = fromPrivateKey(mainAddressKey.getPrivateKeyAsHex(), networkParameters);
        } else {
            ECKey ecKey = ECKey.fromPrivate(mainAddressKey.getPrivKey());
            mainAddress = LegacyAddress.fromKey(networkParameters, ecKey).toBase58();
        }
        String privateKey = mainAddressKey.getPrivateKeyEncoded(networkParameters).toBase58();
        String publicKey = mainAddressKey.getPublicKeyAsHex();
        System.out.println("主钱包地址" + mainAddress);
        System.out.println("私钥:" + privateKey);
        System.out.println("公钥:" + publicKey);
        System.out.println("子钱包1:" + newReceiveAddress(bipPath, xpub, 1, networkParameters).getAddress());
        System.out.println("子钱包2:" + newReceiveAddress(bipPath, xpub, 2, networkParameters).getAddress());
        System.out.println("子钱包3:" + newReceiveAddress(bipPath, xpub, 3, networkParameters).getAddress());
        System.out.println("子钱包20:" + newReceiveAddress(bipPath, xpub, 20, networkParameters).getAddress());
        List<BtcHdWallet.BtcChildAddress> childAddresses = new ArrayList<>();
        //将第一个子地址存入
        childAddresses.add(0, newReceiveAddress(bipPath, xpub, 1, networkParameters));
        return BtcHdWallet.BtcHdWalletBuilder.anBtcHDWallet()
                .withAddress(mainAddress)
                .withPublicKey(publicKey)
                .withPrivateKey(privateKey)
                .withXPub(xpub)
                .withXPrv(xprv)
                .withChildWallet(childAddresses)
                .build();
    }

    public static void main(String[] args) {
        try {
//            DeterministicSeed deterministicSeed = generateSeed("");
//            DeterministicSeed deterministicSeed = getDeterministicSeed(mnemonicString2List(
//                    "crush sight dose guide tennis denial tip network fantasy mushroom loop link"),
//                    "");
//            createHdBtcWallet(deterministicSeed, BIP44Util.BITCOIN_TESTNET_PATH, TestNet3Params.get());
            Script script = new Script(Hex.decode("76a91491e2cedeec26f8e6eae86e72a0158fec5eee8f8a88ac"));
            System.out.println(script);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}